#include <tsubamegaeshi-base/connection.h>
#include <iostream>
#include <cerrno>

Connection::Connection(std::string host="127.0.0.1", int port=3100)
  : host_(host), port_(port)
{
  rcss::net::Addr local(INADDR_ANY,INADDR_ANY);
  socket_.bind(local);

  rcss::net::Addr server(port_,host_);
  socket_.connect(server);
}

Connection::~Connection()
{
}

void
Connection::put_message(const std::string& msg)
{
  //std::cout << "(Connection::put_message)" <<  msg << std::endl;

  if (msg.empty()) {
    return;
  }

  // prefix the message with it's payload length
  unsigned int len = htonl(msg.size());
  std::string prefix((const char*)&len,sizeof(unsigned int));
  std::string str = prefix + msg;
  ::write(socket_.getFD(), str.data(), str.size());
}

bool
Connection::get_message(std::string& msg)
{
#if 0
  static char buffer[16 * 1024];
  static int iMaxLen = 2048;
  unsigned int bytesRead = read( socket_.getFD(), buffer, sizeof(buffer));

  if( bytesRead < sizeof(unsigned int) )
    return false;

  // msg is prefixed with it's total length
  unsigned int msgLen = ntohl(*(unsigned int*)buffer);
  if( msgLen > (unsigned)iMaxLen )
    return false;

  // read remaining message segments
  unsigned int msgRead = bytesRead - sizeof(unsigned int);
  char *offset = buffer + bytesRead;

  while( msgRead < msgLen )
  {
    fd_set fds;
    FD_ZERO( &fds );
    FD_SET( socket_.getFD(), &fds );

    if( select(socket_.getFD() + 1, &fds, 0, 0, 0) > 0 )
    {
      msgRead += read(socket_.getFD(), offset, sizeof(buffer) - msgRead);
      offset += msgRead;
    }
  }

  // zero terminate received data
  (*offset) = 0;
  //::strcpy( (char*)msg, std::string(buffer+sizeof(unsigned int) ).data() );
  msg = std::string(buffer+sizeof(unsigned int));
  return true;
#else
  static char buffer[16 * 1024];

  unsigned int bytesRead = 0;
   while(bytesRead < sizeof(unsigned int)) {
    select_input();
    int readResult = ::read(socket_.getFD(), buffer + bytesRead, sizeof(unsigned int) - bytesRead);
    if(readResult < 0)
      continue;
    bytesRead += readResult;
  }

  //std::cerr << "buffer = |" << string(buffer+1) << "|\n";
  //std::cerr << "bytesRead = |" << bytesRead << "|\n";
  //std::cerr << "Size of buffer = |" << sizeof(buffer) << "|\n";
  //std::cerr << "buffer = |" << buffer << "|\n";
  //std::cerr << "buffer[5] = |" << buffer[5] << "|\n";
  //printf ("xxx-%s\n", buffer+5);

  // msg is prefixed with it's total length
  unsigned int msgLen = ntohl(*(unsigned int*)buffer);
  // std::cerr << "GM 6 / " << msgLen << " (bytesRead " << bytesRead << ")\n";
  if(sizeof(unsigned int) + msgLen > sizeof(buffer)) {
    std::cerr << "too long message; aborting" << std::endl;
    ::abort();
  }

  // read remaining message segments
  unsigned int msgRead = bytesRead - sizeof(unsigned int);

  //std::cerr << "msgRead = |" << msgRead << "|\n";

  char *offset = buffer + bytesRead;

  while (msgRead < msgLen) {
    if (! select_input()) {
      return false;
    }

    int readLen = sizeof(buffer) - msgRead;
    if(readLen > msgLen - msgRead) {
      readLen = msgLen - msgRead;
    }

    int readResult = read(socket_.getFD(), offset, readLen);
    if(readResult < 0) {
      continue;
    }

    msgRead += readResult;
    offset += readResult;
    //std::cerr << "msgRead = |" << msgRead << "|\n";
  }

  // zero terminate received data
  (*offset) = 0;

  msg = std::string(buffer+sizeof(unsigned int));

  // DEBUG
  //std::cout << msg << std::endl;

  return true;
#endif
}

bool
Connection::select_input()
{
    fd_set readfds;
    struct timeval tv = {60,0};
    FD_ZERO(&readfds);
    FD_SET(socket_.getFD(), &readfds);
    
    while(1) {
        switch(select(socket_.getFD()+1, &readfds, 0, 0, &tv)) {
        case 1:
            return 1;
        case 0:
            std::cerr << "(Connection::select_input) select failed " << strerror(errno) << std::endl;
            ::abort();
            return 0;
        default:
            if(errno == EINTR)
                continue;
            std::cerr << "(Connection::select_input) select failed " << strerror(errno) << std::endl;
            ::abort();
            return 0;
        }
    }
}

void
Connection::close()
{
  socket_.close();
}
